package org.krivtsun.scanlab.editor;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.logging.Logger;

/**
 * todo Optimize image showing speed (compare Image.getScaledInstance() and AffineTransform methods)
 *
 * @author Andrew Krivtsun
 * @see <a href="http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">The Perils of Image.getScaledInstance()</a>
 */
class ImageView extends JPanel {

    private static final Dimension ZERO_SIZE = new Dimension();

    private RenderedImage img;
    private RenderedImage outputImg;
    private Dimension prefSize = ZERO_SIZE;
    private JLabel statusBarItem = new JLabel();
    private int height;

    JComponent getStatusBarItem() {
        return statusBarItem;
    }

    void setImage(RenderedImage img) {
        this.img = img;
        updateStatusBar();
        setScale(1);
    }

    @Override
    public void setLocation(int x, int y) {
        super.setLocation(x, y);
        System.out.println("x=" + x + ", y=" + y);
    }

    @Override
    public Dimension getPreferredSize() {
        return prefSize;
    }

    /**
     * Convenience method that returns a scaled instance of the
     * provided {@code BufferedImage}.
     *
     * @param img           the original image to be scaled
     * @param targetWidth   the desired width of the scaled instance,
     *                      in pixels
     * @param targetHeight  the desired height of the scaled instance,
     *                      in pixels
     * @param hint          one of the rendering hints that corresponds to
     *                      {@code RenderingHints.KEY_INTERPOLATION} (e.g.
     *                      {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
     *                      {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
     *                      {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param higherQuality if true, this method will use a multi-step
     *                      scaling technique that provides higher quality than the usual
     *                      one-step technique (only useful in downscaling cases, where
     *                      {@code targetWidth} or {@code targetHeight} is
     *                      smaller than the original dimensions, and generally only when
     *                      the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    public RenderedImage getScaledInstance(RenderedImage img,
                                           int targetWidth,
                                           int targetHeight,
                                           Object hint,
                                           boolean higherQuality) {
        int type = BufferedImage.TYPE_INT_RGB;
        RenderedImage ret = img;
        int w, h;
        if (higherQuality) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }

        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawRenderedImage(ret, new AffineTransform());
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }


    void setScale(double scale) {
        if (img != null) {
            long t = System.currentTimeMillis();
//            AffineTransform trans = AffineTransform.getScaleInstance(scale, scale);
//            AffineTransformOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
//            outputImg = op.filter(img, null);
            int scaledWidth = (int) (img.getWidth() * scale);
            int scaledHeight = (int) (img.getHeight() * scale);
//            outputImg = img.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
            outputImg = getScaledInstance(img, scaledWidth, scaledHeight,
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);

            Logger.global.info("Scale image time = " + (System.currentTimeMillis() - t));
            prefSize = new Dimension(scaledWidth, scaledHeight);
        } else {
            prefSize = ZERO_SIZE;
        }

        revalidate();
        repaint();
    }

    public void paintComponent(java.awt.Graphics g) {

        super.paintComponent(g);

//        Rectangle drawHere = g.getClipBounds();
//        g.setColor(getBackground());
//        //g.fillRect(0, 0, getWidth(), getHeight());
//        g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);

        if (outputImg != null) {
            Graphics2D g2D = (Graphics2D) g;
            long t = System.currentTimeMillis();
            //g.drawImage(outputImg, 0,0, null);
            g2D.setColor(Color.green);
            Rectangle cr = g2D.getClipBounds();
            System.out.println("cr=" + cr);
            System.out.println("getX()=" + getX());
            System.out.println("getY()=" + getY());
//            System.out.println("getWidth()="+getWidth());
//            System.out.println("getHeight()="+getHeight());
//            g.fillRect(cr.x,cr.y, cr.width,cr.height);

//            CropImageFilter cropimagefilter = new CropImageFilter(cr.x,cr.y, cr.x+cr.width,cr.y+cr.height);
//            ImageProducer imgProd = outputImg.getSource();
//            imgProd = new FilteredImageSource(imgProd, cropimagefilter);
//            getToolkit().createImage(imgProd);
//            g2D.drawImage(getToolkit().createImage(imgProd), 0, 0, null);
            g2D.drawRenderedImage(outputImg, new AffineTransform());
            Logger.global.info("drawImage time = " + (System.currentTimeMillis() - t));
        }
    }

    private void updateStatusBar() {
        statusBarItem.setText(img == null
                ? null
                : img.getWidth() + "x" + img.getHeight());
    }
}
